/// <reference path="../utilities/FXDom.js" />
/// <reference path="../utilities/Xpcom.js" />
///--------------------------------------------------------------------
/// <file name="MarkupTranslator.js">
///   Translates HTML to markup using translation rules retrieved from Neoseeker.
/// </file>
///--------------------------------------------------------------------

NAMFox.ns(function() { with (NAMFox) {

this.MarkupTranslator = {
    getMarkupFromHtml: function(htmlDocument, html, callback) {
        if ($.getConfig().isInTestMode()) {
            // In test mode, use the local translation rule-set, not the one on the site.
            this._translate(this, { translationRules: translationRules }, html, callback);
        }
        else {
            Sandbox.evalOnPage(
                htmlDocument,
                "MarkupTranslator._translate(MarkupTranslator, NAMFox, html, callback)",
                {
                    html: html,
                    callback: callback,
                    MarkupTranslator: this,
                    NAMFox: "NAMFox"
                });
        }
    },

    _replaceList: function(hierarchy, startingIndex, depth, markup, lastTags) {
        /// <summary>
        ///   Converts a single wikilist to the appropriate markup.
        /// </summary>
        /// <param name="hierarchy" type="String">
        ///   The string containing the list tags to translate.
        /// </param>
        /// <param name="startingIndex" type="Number" integer="true">
        ///   The index in the string that represents at what
        ///   index the scanner is currently set.
        /// </param>
        /// <param name="depth" type="Number" integer="true">
        ///   The depth in the list the scanner is currently at.
        /// </param>
        /// <param name="markup" type="String">
        ///   The new markup string to return.
        /// </param>
        /// <param name="lastTags" type="Array">
        ///   An array of strings (either '#' or '*') that keeps track of
        ///   what element exists on what depth level.
        /// </param>
        /// <returns type="String" />
        
        var tag = hierarchy.substr(startingIndex, 4);
        
        if (tag.indexOf("ul") !== -1) {
            markup += $.repeat("*", depth);
            lastTags[depth - 1]  = "*";
        }
        else if (tag.indexOf("ol") !== -1) {
            markup += $.repeat("#", depth);
            lastTags[depth - 1] = "#";
        }
        else if (tag.indexOf("li") !== -1) {
            markup += $.repeat(lastTags[depth - 1], depth);
            startingIndex++;
        }
        
        markup += " ";
        
        // Increment by 9: "<ul><li> " or "/li><li> "
        startingIndex += 9;
        
        var liNextStep = hierarchy.indexOf("</li>", startingIndex);
        if (liNextStep === -1) {
            liNextStep = 1000000;
        }
        
        var ulNextStep = hierarchy.indexOf("<ul>", startingIndex);
        if (ulNextStep === -1) {
            ulNextStep = 1000000;
        }
        
        var olNextStep = hierarchy.indexOf("<ol>", startingIndex);
        if (olNextStep === -1) {
            olNextStep = 1000000;
        }
        
        var nextStep = -1;
        if (liNextStep < ulNextStep) {
            if (liNextStep < olNextStep) {
                nextStep = liNextStep;
            }
            else {
                nextStep = olNextStep;
            }
        }
        else {
            if (ulNextStep < olNextStep) {
                nextStep = ulNextStep;
            }
            else {
                nextStep = olNextStep;
            }
        }
        
        markup += hierarchy.substr(startingIndex, nextStep - startingIndex);
        startingIndex = nextStep;
        
        var nextTag = hierarchy.substr(startingIndex, 4);
        
        if (nextTag.indexOf("/li") !== -1) {
            while (
                (nextTag = hierarchy.substr(startingIndex, 4)) !== ""
                && nextTag.indexOf("<li>") === -1
            ) {
                startingIndex += 5;
                if (nextTag.match(/[ou]l/)) {
                    lastTags[depth - 1] = "";
                    --depth;
                }
            }
            if (nextTag === "") {
                // reached end of the string.
                return markup;
            } else {
                // Reached <li> tag
                return this._replaceList(hierarchy, startingIndex - 5, depth, markup + "\n", lastTags);
            }
        }
        else {
            // Another open tag, redo our analysis.
            return this._replaceList(hierarchy, startingIndex, ++depth, markup + "\n", lastTags);
        }
    },
    
    _replaceLists: function(html) {
        /// <summary>
        ///   Converts the new wiki-lists into their nTag equivalents.
        /// </summary>
        /// <param name="html" type="String">
        ///   The HTML in which to find and replace wikilists.
        /// </param>
        /// <returns type="String" />
        
        var listExpr = /(?:<span class="wikilists">)?<[ou]l>(?:<font size="\d">)*<li>(?=.*<\/?(?:[ou]l|li)).*(?:<\/span><\!-- wikilists -->)?/i;
        for (var results = html.match(listExpr); results; results = html.match(listExpr)) {
            var hierarchy = results[0].replace(/(?:<p>|\n)/g, "");
            var filteredHierarchy = hierarchy.replace(/<span class="wikilists">/g, "").replace(/<\/span><\!-- wikilists -->/g, "");
            
            // In Firefox 3, sometimes the DOM model can become bloated with excess tags that actually appear
            // between the <[ou]l> and <li> or between the </li> and </[ou]l>. This replacement switches the
            // content to a form that interpretList() can account for.
            filteredHierarchy = filteredHierarchy.replace(/<([ou])l>(.*?)<li> /g, "<$1l><li> $2");
            filteredHierarchy = filteredHierarchy.replace(/<\/li>(.*?)<(li|\/[ou]l)>/g, "$1</li><$2>");
            
            // Sometimes we also see legacy tags that don't insert a trailing </li> tag. This normalization will
            // work in these cases.
            filteredHierarchy = filteredHierarchy.replace(/<li>([^><]*?)(<\/[ou]l>)/g, "<li>$1</li>$2");
            
            var list = this._replaceList(filteredHierarchy, 0, 1, "", []);
            html = html.replace(new RegExp(hierarchy.replace(/([\\\^\$*+[\]?{}.=!:(|)])/g, "\\$1"), "g"), list);
        }
        
        return html;
    },

    _replaceSmileys: function(html, data) {
        /// <summary>
        ///   Replaces smileys with their respective shortcut equivalents in text.
        /// </summary>
        /// <param name="html" type="String">
        ///   The HTML string that should be replaced.
        /// </param>
        /// <param name="argument" type="Object">
        ///   Contains information about how smileys should be replaced.
        /// </param>
        
        return html.replace(
            new RegExp(data.regexp.source, "gm"),
            function (all, smileyFileName) {
                var smiley = data.smileyMap[smileyFileName];
                return smiley || "";
            }
        );
    },

    _stripPhpFontTags: function(html) {
        /// <summary>
        ///   Strips the font tags that result from PHP's highlight_string() function when converting
        ///   [php] tag content into markup.
        /// </summary>
        /// <param name="html" type="String">The HTML to convert.</param>
        /// <returns type="String" />
        
        // Needs to run through this regexp multiple times
        // (should only be two though) because there is one font tag that
        // usually stays at the beginning and encompasses
        // the entire php code section.
        var regexp = /<font color=".*?">([\s\S]*?)<\/font>/gi;
        var i = 0;
        while (regexp.test(html)) {
            html = html.replace(regexp, '$1');
        }
        
        var phpSectionRegexp = /<code>([\s\S]*?)<\/code>/gi;
        var php5Regexp = /<span style=".*?">([\s\S]*?)<\/span>/gi;
        
        // Use a temporary variable to make the replacements, because
        // phpSectionRegexp's lastIndex should be preserved against the
        // original string so that multiple PHP tags can be found. This
        // is actually what caused Issue #111.
        var newHtml = html;
        while (results = phpSectionRegexp.exec(html)) {
            var phpSection = results[1];
            
            // Translate twice to account for nested tags.
            var newPhpSection = phpSection.replace(php5Regexp, "$1");
            newPhpSection = newPhpSection.replace(php5Regexp, "$1");
            
            newHtml = newHtml.replace($.toRegExp(phpSection, "gi"), newPhpSection);
        }
        
        return newHtml;
    },

    _translate: function(markupTranslator, namfoxObject, html, callback) {
        var translationRules = namfoxObject.translationRules;
        $.assert(translationRules, "Could not find translation rules on Neoseeker.");

        for (var i = 0; i < translationRules.length; ++i) {
            var rule = translationRules[i];

            if (rule.regexp) {
                $.assert(rule.replacement !== undefined, "When a translation rule has a regular expression it should also have a replacement expression. Rule name: " + rule.name);

                // Have to recompile the RegExp because String.replace() doesn't work with a RegExp
                // accessed through the sandbox, since it doesn't know how to handle a proxy to the RegExp
                // instead of the RegExp itself.
                var regexp = new RegExp(rule.regexp.source, "g");

                if (rule.repeatTillMatchless) {
                    while (regexp.test(html)) {
                        regexp.lastIndex = 0;
                        html = html.replace(regexp, rule.replacement);
                    }
                }
                else {
                    html = html.replace(regexp, rule.replacement);
                }
            }
            else if (rule.callFunction) {
                html = markupTranslator[rule.callFunction](html, rule.argument);
            }
        }

        callback(html);
    }
};

}});